package com.zbs.framework.dao.user.service.impl;

import com.zbs.framework.common.utils.CommonUtil;
import com.zbs.framework.common.utils.Constant;
import com.zbs.framework.common.utils.ErrEnum;
import com.zbs.framework.common.utils.ErrorAssert;
import com.zbs.framework.dao.passport.base.service.AccountService;
import com.zbs.framework.dao.passport.entity.MNNormal;
import com.zbs.framework.dao.passport.normal.repository.MNNormalRepository;
import com.zbs.framework.dao.passport.normal.service.NormalAccountService;
import com.zbs.framework.dao.user.UserConst;
import com.zbs.framework.dao.user.entity.MNBaseUser;
import com.zbs.framework.dao.user.entity.UserNormalAccComb;
import com.zbs.framework.dao.user.event.UserDeleteEvent;
import com.zbs.framework.dao.user.event.UserEditEvent;
import com.zbs.framework.dao.user.repository.BaseUserRepository;
import com.zbs.framework.dao.user.repository.UserNormalAccCombRepository;
import com.zbs.framework.dao.user.repository.UserRoleRepository;
import com.zbs.framework.dao.user.service.BaseUserService;
import com.zbs.framework.dao.user.service.UserManageService;
import com.zbs.framework.dao.utils.jpa.JpaQueryWrapper;
import com.zbs.framework.security.config.FrameworkConstants;
import com.zbs.framework.security.utils.TokenManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * Created by Zbs
 * 2018-11-23 17:24
 */
@Service
@Transactional
public class UserManageServiceImpl implements UserManageService {

    @Autowired
    private BaseUserService baseUserService;
    @Autowired
    private BaseUserRepository baseUserRepository;
    @Autowired
    private UserRoleRepository userRoleRepository;
    @Autowired
    private UserNormalAccCombRepository userNormalAccCombRepository;
    @Autowired
    private NormalAccountService normalAccountService;
    @Autowired
    private AccountService accountService;
    @Autowired
    private MNNormalRepository normalRepository;
    @Autowired
    private ApplicationContext applicationContext;
    @PersistenceContext
    private EntityManager entityManager;

    @Override
    public MNBaseUser createUser(String username, String pwd, Integer[] roleIds,
                                 String name, Character gender, String birthday,
                                 String headImg, String phone) {
        // 参数检查
        ErrorAssert.hasLength(name, "Name can not be empty!");
        ErrorAssert.isTrue(gender.equals(UserConst.MALE) || gender.equals(UserConst.FEMALE),
                "Illegal value of gender!");
        // 创建账号
        MNNormal normal = normalAccountService.create(username, pwd);
        // 创建用户
        return baseUserService.createUser(normal.getAcctUuid(), roleIds, name, gender,
                birthday, headImg, phone);
    }

    @Override
    public MNBaseUser editUser(Long userId, Integer[] roleIds,
                               String name, Character gender, String birthday,
                               String headImg, String phone) {
        // 参数检查
        ErrorAssert.hasLength(name, "Name can not be empty!");
        ErrorAssert.isTrue(gender.equals(UserConst.MALE) || gender.equals(UserConst.FEMALE),
                "Illegal value of gender!");
        // 查找用户
        MNBaseUser baseUser = baseUserRepository.findById(userId).orElse(null);
        ErrorAssert.notNull(baseUser, ErrEnum.USER_NOT_EXIST);
        // 修改用户角色信息
        baseUserService.saveUserRoles(userId, roleIds);
        // 修改其他信息
        baseUser.setName(name);
        baseUser.setGender(gender);
        baseUser.setBirthday(birthday);
        baseUser.setHeadImg(headImg);
        baseUser.setPhone(phone);
        baseUserRepository.save(baseUser);
        return baseUser;
    }

    @Override
    public void setUserEnabled(Long userId, Integer enabled) {
        MNBaseUser user = getUser(userId);
        // 检查用户可编辑状态
        assertUserEditable(userId);
        // 修改用户account状态
        accountService.setEnabled(user.getAcctUuid(), enabled);

        this.applicationContext.publishEvent(new UserEditEvent(user.getId(),user.getAcctUuid()));
    }

    @Override
    public void forceChangeAccUsername(Long userId, String username) {
        MNBaseUser user = getUser(userId);
        // 检查用户可编辑状态
        assertUserEditable(userId);
        // 修改账号
        normalAccountService.forceChangeUsername(user.getAcctUuid(), username);
    }

    @Override
    public void forceChangePwd(Long userId, String pwd) {
        MNBaseUser user = getUser(userId);
        // 检查用户可编辑状态
        assertUserEditable(userId);
        // 修改密码
        normalAccountService.forceChangePwd(user.getAcctUuid(), pwd);
    }

    @Override
    public void forceChangePwd(String username, String pwd) {
        ErrorAssert.hasLength(username,"用户名不能为空");
        ErrorAssert.hasLength(pwd,"密码不能为空");
        MNNormal mnNormal = new JpaQueryWrapper<>(MNNormal.class,entityManager).eq(MNNormal::getUsername, username)
                .eq(MNNormal::getDeleted,Constant.YES).get();
        ErrorAssert.notNull(mnNormal,"用户不存在，修改失败");
        mnNormal.setPwd(CommonUtil.MD5_32(pwd));
        this.normalRepository.save(mnNormal);
    }

    @Override
    public void forceChangePhone(Long userId, String phone) {
        MNBaseUser user = this.getUser(userId);
        user.setPhone(phone);
        this.baseUserRepository.save(user);
    }

    @Override
    public MNBaseUser getUser(Long userId) {
        // 参数检查
        ErrorAssert.notNull(userId, "User id can not be null!");
        //
        MNBaseUser user = baseUserRepository.findById(userId).orElse(null);
        ErrorAssert.notNull(user, "User not exist!");
        return user;
    }

    @Override
    public MNBaseUser getUser(String username) {
        MNNormal mnNormal = new JpaQueryWrapper<>(MNNormal.class,entityManager).eq(MNNormal::getUsername, username)
                .eq(MNNormal::getDeleted,Constant.YES).get();
        ErrorAssert.notNull(mnNormal,"用户不存在");
        return this.baseUserRepository.findByAcctUuid(mnNormal.getAcctUuid());
    }

    @Override
    public void batchDeleteUser(List<Long> userIdList) {
        if (!CollectionUtils.isEmpty(userIdList)) {
            userIdList.forEach(userId -> {
                MNBaseUser mnBaseUser = this.baseUserRepository.findById(userId).orElse(null);
                if (mnBaseUser != null){
                    ErrorAssert.isTrue(!Arrays.asList(FrameworkConstants.DEFAULT_ADMIN).contains(userId),String.format("用户`%s`不可被删除",mnBaseUser.getName()));
                    List<MNNormal> mnNormalList = this.normalRepository.findAllByAcctUuid(mnBaseUser.getAcctUuid());
                    mnNormalList.forEach(mnNormal -> {
                        mnNormal.setDeleted(Constant.NO);
                        this.normalRepository.save(mnNormal);
                        TokenManager.clearAllToken(mnNormal.getUsername());
                    });
                    this.applicationContext.publishEvent(new UserDeleteEvent(userId));
                }
            });
        }
    }

    @Override
    public void deleteUserByUUid(String uuid) {
        ErrorAssert.hasLength(uuid,"UUID 不能为空");
        MNBaseUser mnBaseUser = this.baseUserRepository.findByAcctUuid(uuid);
        if (mnBaseUser != null){
            ErrorAssert.isTrue(!Arrays.asList(FrameworkConstants.DEFAULT_ADMIN).contains(mnBaseUser.getId()),String.format("用户`%s`不可被删除",mnBaseUser.getName()));
            List<MNNormal> mnNormalList = this.normalRepository.findAllByAcctUuid(mnBaseUser.getAcctUuid());
            mnNormalList.forEach(mnNormal -> {
                mnNormal.setDeleted(Constant.NO);
                this.normalRepository.save(mnNormal);
                TokenManager.clearAllToken(mnNormal.getUsername());
            });
            this.applicationContext.publishEvent(new UserDeleteEvent(mnBaseUser.getId()));
        }
    }

    @Override
    public Integer[] getUserRoles(Long userId) {
        // 参数检查
        ErrorAssert.notNull(userId, "User id can not be null!");
        //
        return userRoleRepository.findRoleIds(userId);
    }

    @Override
    public Page<UserNormalAccComb> findUserPage(Integer roleId, Integer enabled, String fuzzUsername, Pageable pageable) {
        Specification<UserNormalAccComb> specification = new Specification<UserNormalAccComb>() {
            @Nullable
            @Override
            public Predicate toPredicate(Root<UserNormalAccComb> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                List<Predicate> list = new ArrayList<>();
                //
                if (StringUtils.hasLength(fuzzUsername)){
                    Predicate or = cb.or(
                            cb.like(root.get("username"), "%" + fuzzUsername + "%"),
                            cb.like(root.get("name"), "%" + fuzzUsername + "%"),
                            cb.like(root.get("phone"), "%" + fuzzUsername + "%")
                    );
                    list.add(or);
                }
                // 启用状态
                if (enabled != null) {
                    list.add(cb.equal(root.get("enabled"), enabled));
                }
                // 角色
                if (roleId != null) {
                    list.add(cb.equal(root.joinList("roles").get("id"), roleId));
                }
                return cb.and(list.toArray(new Predicate[]{}));
            }
        };
        return userNormalAccCombRepository.findAll(specification, pageable);
    }

    void assertUserEditable(Long userId) {
        if (Arrays.asList(FrameworkConstants.DEFAULT_ADMIN).contains(userId)) {
            ErrorAssert.error("此用户不可被修改");
        }
//        List<Role> roleIds = userRoleRepository.findUserRoles(userId);
//        if (roleIds == null) return;
//        for (Role role : roleIds) {
//            ErrorAssert.isTrue(!role.getEditable().equals(UserConst.ROLE_SUPER_ADMIN),
//                    ErrEnum.CANT_EDIT_ROLE_OF_SUPER_ADMIN);
//        }
    }
}
